package com.ruoyi.business.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * S7协议诊断工具类
 * 用于分析和解决S7连接问题
 * 
 * @author qiancheng
 * @date 2025-09-01
 */
public class S7DiagnosticUtils {

    private static final Logger log = LoggerFactory.getLogger(S7DiagnosticUtils.class);
    
    // 连接诊断统计
    private static final Map<String, AtomicInteger> connectionAttempts = new ConcurrentHashMap<>();
    private static final Map<String, AtomicInteger> connectionFailures = new ConcurrentHashMap<>();
    private static final Map<String, AtomicInteger> readTimeouts = new ConcurrentHashMap<>();
    private static final Map<String, Long> lastConnectionTime = new ConcurrentHashMap<>();
    
    // 错误码映射
    private static final Map<Integer, String> S7_ERROR_CODES = new HashMap<>();
    
    static {
        // 初始化S7错误码映射
        S7_ERROR_CODES.put(0x00, "无错误");
        S7_ERROR_CODES.put(0x01, "硬件错误");
        S7_ERROR_CODES.put(0x03, "不允许访问");
        S7_ERROR_CODES.put(0x04, "地址错误/访问被拒绝");
        S7_ERROR_CODES.put(0x05, "数据类型不支持");
        S7_ERROR_CODES.put(0x06, "数据类型不一致");
        S7_ERROR_CODES.put(0x07, "对象不存在");
        S7_ERROR_CODES.put(0x0A, "对象已存在");
        S7_ERROR_CODES.put(0xFF, "成功");
    }
    
    /**
     * 记录连接尝试
     */
    public static void recordConnectionAttempt(String plcIp) {
        connectionAttempts.computeIfAbsent(plcIp, k -> new AtomicInteger(0)).incrementAndGet();
        lastConnectionTime.put(plcIp, System.currentTimeMillis());
        log.debug("PLC [{}] 连接尝试次数: {}", plcIp, connectionAttempts.get(plcIp).get());
    }
    
    /**
     * 记录连接失败
     */
    public static void recordConnectionFailure(String plcIp, String reason) {
        connectionFailures.computeIfAbsent(plcIp, k -> new AtomicInteger(0)).incrementAndGet();
        log.warn("PLC [{}] 连接失败 (累计{}次): {}", plcIp, 
                connectionFailures.get(plcIp).get(), reason);
        
        // 分析失败原因并给出建议
        analyzeConnectionFailure(plcIp, reason);
    }
    
    /**
     * 记录读取超时
     */
    public static void recordReadTimeout(String plcIp) {
        readTimeouts.computeIfAbsent(plcIp, k -> new AtomicInteger(0)).incrementAndGet();
        log.warn("PLC [{}] 读取超时 (累计{}次)", plcIp, readTimeouts.get(plcIp).get());
    }
    
    /**
     * 解析S7错误码
     */
    public static String parseS7ErrorCode(int errorCode) {
        return S7_ERROR_CODES.getOrDefault(errorCode, "未知错误码: 0x" + Integer.toHexString(errorCode));
    }
    
    /**
     * 分析连接失败原因
     */
    private static void analyzeConnectionFailure(String plcIp, String reason) {
        if (reason == null) return;
        
        String lowerReason = reason.toLowerCase();
        
        if (lowerReason.contains("connection reset") || lowerReason.contains("连接重置")) {
            log.error("🔧 诊断建议 [{}]: PLC主动断开连接，可能原因:", plcIp);
            log.error("   1. PLC连接数已达上限，建议减少并发连接");
            log.error("   2. PLC负载过重，建议增加读取间隔");
            log.error("   3. 网络不稳定，检查网络连接");
        } else if (lowerReason.contains("timeout") || lowerReason.contains("超时")) {
            log.error("🔧 诊断建议 [{}]: 连接或读取超时，可能原因:", plcIp);
            log.error("   1. 网络延迟高，建议增加超时时间");
            log.error("   2. PLC响应慢，检查PLC负载");
            log.error("   3. 防火墙阻止连接，检查端口102是否开放");
        } else if (lowerReason.contains("refused") || lowerReason.contains("拒绝")) {
            log.error("🔧 诊断建议 [{}]: 连接被拒绝，可能原因:", plcIp);
            log.error("   1. PLC IP地址错误");
            log.error("   2. PLC端口错误（默认102）");
            log.error("   3. 机架/槽位配置错误");
            log.error("   4. PLC S7通信未启用");
        }
    }
    
    /**
     * 生成PLC诊断报告
     */
    public static Map<String, Object> generateDiagnosticReport(String plcIp) {
        Map<String, Object> report = new HashMap<>();
        
        // 基本统计
        int attempts = connectionAttempts.getOrDefault(plcIp, new AtomicInteger(0)).get();
        int failures = connectionFailures.getOrDefault(plcIp, new AtomicInteger(0)).get();
        int timeouts = readTimeouts.getOrDefault(plcIp, new AtomicInteger(0)).get();
        long lastConnection = lastConnectionTime.getOrDefault(plcIp, 0L);
        
        report.put("plcIp", plcIp);
        report.put("connectionAttempts", attempts);
        report.put("connectionFailures", failures);
        report.put("readTimeouts", timeouts);
        report.put("lastConnectionTime", lastConnection);
        
        // 计算成功率
        double successRate = attempts > 0 ? ((double)(attempts - failures) / attempts) * 100 : 0;
        report.put("successRate", String.format("%.1f%%", successRate));
        
        // 健康状态评估
        String healthStatus;
        if (successRate >= 90) {
            healthStatus = "健康";
        } else if (successRate >= 70) {
            healthStatus = "一般";
        } else if (successRate >= 50) {
            healthStatus = "较差";
        } else {
            healthStatus = "异常";
        }
        report.put("healthStatus", healthStatus);
        
        // 建议
        List<String> recommendations = generateRecommendations(plcIp, successRate, timeouts);
        report.put("recommendations", recommendations);
        
        return report;
    }
    
    /**
     * 生成优化建议
     */
    private static List<String> generateRecommendations(String plcIp, double successRate, int timeouts) {
        List<String> recommendations = new ArrayList<>();
        
        if (successRate < 50) {
            recommendations.add("❗ 连接成功率过低，检查PLC网络连接和配置");
            recommendations.add("💡 验证PLC IP地址、端口、机架和槽位配置");
            recommendations.add("🔧 检查PLC是否启用S7通信功能");
        } else if (successRate < 70) {
            recommendations.add("⚠️ 连接不稳定，建议优化网络环境");
            recommendations.add("💡 增加连接重试间隔时间");
        }
        
        if (timeouts > 10) {
            recommendations.add("⏰ 读取超时频繁，建议增加读取超时时间");
            recommendations.add("📊 检查PLC负载，避免在PLC繁忙时读取");
        }
        
        if (connectionFailures.getOrDefault(plcIp, new AtomicInteger(0)).get() > 5) {
            recommendations.add("🔄 连接失败次数较多，建议减少连接频率");
            recommendations.add("🎯 使用单连接模式，避免并发连接冲突");
        }
        
        if (recommendations.isEmpty()) {
            recommendations.add("✅ 连接状态良好，无需特别优化");
        }
        
        return recommendations;
    }
    
    /**
     * 获取所有PLC的诊断统计
     */
    public static Map<String, Object> getAllDiagnosticStats() {
        Map<String, Object> allStats = new HashMap<>();
        
        Set<String> allPlcIps = new HashSet<>();
        allPlcIps.addAll(connectionAttempts.keySet());
        allPlcIps.addAll(connectionFailures.keySet());
        allPlcIps.addAll(readTimeouts.keySet());
        
        for (String plcIp : allPlcIps) {
            allStats.put(plcIp, generateDiagnosticReport(plcIp));
        }
        
        return allStats;
    }
    
    /**
     * 重置指定PLC的统计信息
     */
    public static void resetStats(String plcIp) {
        connectionAttempts.remove(plcIp);
        connectionFailures.remove(plcIp);
        readTimeouts.remove(plcIp);
        lastConnectionTime.remove(plcIp);
        log.info("已重置PLC [{}] 的诊断统计信息", plcIp);
    }
    
    /**
     * 重置所有统计信息
     */
    public static void resetAllStats() {
        connectionAttempts.clear();
        connectionFailures.clear();
        readTimeouts.clear();
        lastConnectionTime.clear();
        log.info("已重置所有PLC诊断统计信息");
    }
    
    /**
     * 检查PLC配置合理性
     */
    public static List<String> validatePlcConfiguration(String plcIp, int port, int rack, int slot) {
        List<String> issues = new ArrayList<>();
        
        // 验证IP地址格式
        if (!isValidIP(plcIp)) {
            issues.add("IP地址格式无效: " + plcIp);
        }
        
        // 验证端口范围
        if (port < 1 || port > 65535) {
            issues.add("端口号超出有效范围 (1-65535): " + port);
        } else if (port != 102) {
            issues.add("警告: S7协议标准端口为102，当前配置为: " + port);
        }
        
        // 验证机架和槽位
        if (rack < 0 || rack > 7) {
            issues.add("机架号超出有效范围 (0-7): " + rack);
        }
        
        if (slot < 0 || slot > 31) {
            issues.add("槽位号超出有效范围 (0-31): " + slot);
        }
        
        return issues;
    }
    
    /**
     * 验证IP地址格式
     */
    private static boolean isValidIP(String ip) {
        if (ip == null || ip.trim().isEmpty()) {
            return false;
        }
        
        String[] parts = ip.split("\\.");
        if (parts.length != 4) {
            return false;
        }
        
        try {
            for (String part : parts) {
                int num = Integer.parseInt(part);
                if (num < 0 || num > 255) {
                    return false;
                }
            }
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
}